package com.mbti.youmore.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mbti.youmore.entity.*;
import com.mbti.youmore.mapper.*;
import com.mbti.youmore.service.StatisticsService;
import com.mbti.youmore.util.ProvinceMapperUtil;
import com.mbti.youmore.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StatisticsServiceImpl implements StatisticsService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PostMapper postMapper;

    @Autowired
    private LikeMapper likeMapper;

    @Autowired
    private FollowMapper followMapper;

    @Autowired
    private FavoriteMapper favoriteMapper;

    @Autowired
    private CommentMapper commentMapper;

    /**
     * 排行榜刷新时间
     */
    private static final Integer REFRESH_TIME = 1000 * 60 * 5;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private CommentLikeMapper commentLikeMapper;

    @Override
    public List<Map<String, Object>> getUserStatistics() {
        List<User> users = userMapper.selectList(null);
        List<Map<String, Object>> res = new ArrayList<>();
        List<Map<String, Object>> provinceMapList = ProvinceMapperUtil.getProvinceMapList();
        for (Map<String, Object> map : provinceMapList) {
            Map<String, Object> one = new HashMap<>();
            String name = (String) map.get("name");
            int cnt = 0;
            for (User user : users) {
                if (user.getUserLocation().equals(name)) {
                    cnt++;
                }
            }
            one.put("name", name);
            List<Object> value = new ArrayList<>();
            value.add(map.get("lon"));
            value.add(map.get("lat"));
            value.add(cnt);
            one.put("value", value);
            res.add(one);
        }
        return res;
    }

    @Override
    public List<Integer> getPostNum() {
        Map<String, Object> result = new LinkedHashMap<>();
        List<Map<String, Integer>> datas = new ArrayList<>();
        List<Post> posts = postMapper.selectList(null);
        Timestamp endTimestamp = new Timestamp(System.currentTimeMillis());
        List<Long> timeLimit = new ArrayList<>();
        for (int i = 1; i <= 7; i++) {
            Map<String, Integer> data = new LinkedHashMap<>();
            data.put("id", i);
            data.put("num", 0);
            datas.add(data);
            timeLimit.add((long) i * 1000 * 60 * 60 * 24);
        }
        for (Map<String, Integer> data : datas) {
            int num = 0;
            for (Post post : posts) {
                if (data.get("id") == 1) {
                    if (endTimestamp.getTime() - post.getPostTime().getTime() < timeLimit.get(data.get("id") - 1)) {
                        num++;
                    }
                } else {
                    if ((endTimestamp.getTime() - post.getPostTime().getTime()) < timeLimit.get(data.get("id") - 1) && (endTimestamp.getTime() - post.getPostTime().getTime()) >= timeLimit.get(data.get("id") - 2)) {
                        num++;
                    }
                }
            }
            data.replace("num", num);
        }
        List<Integer> res = new ArrayList<>();
        for (Map<String, Integer> data : datas) {
            res.add(data.get("num"));
        }
        Collections.reverse(res);
        return res;
    }

    @Override
    public List<Map<String, Object>> getHotUserList(Integer count, Integer userId) throws Exception {
        /*
         * 用户热度 = 粉丝数 * 0.3 + 发帖量 * 0.3 + 点赞数 * 0.15 + 收藏量 * 0.1 + 评论数 * 0.1 + 关注量 * 0.05
         */
        List<User> users = userMapper.selectList(null);

        // 将用户对象列表转为Map对象
        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(users);
        List<Map<String, Object>> userMapList = objectMapper.readValue(s, new TypeReference<>() {
        });

        userMapList.sort((user1, user2) -> {
            // 获取粉丝数
            List<User> fans1 = followMapper.selectByFollowedUserId((Integer) user1.get("userId"));
            List<User> fans2 = followMapper.selectByFollowedUserId((Integer) user2.get("userId"));
            user1.put("fansNum", fans1.size());
            user2.put("fansNum", fans2.size());
            // 获取发帖量
            List<Post> posts1 = postMapper.selectByUserId((Integer) user1.get("userId"));
            List<Post> posts2 = postMapper.selectByUserId((Integer) user2.get("userId"));
            user1.put("PostNum", posts1.size());
            user2.put("PostNum", posts2.size());
            // 获取被点赞数
            List<Comment> comments1 = commentMapper.selectList(new LambdaQueryWrapper<Comment>().eq(Comment::getUserId, user1.get("userId")));
            List<Comment> comments2 = commentMapper.selectList(new LambdaQueryWrapper<Comment>().eq(Comment::getUserId, user2.get("userId")));
            int likeNum1 = CollectionUtils.isEmpty(comments1) ? 0 : Math.toIntExact(commentLikeMapper.selectCount(new LambdaQueryWrapper<CommentLike>().in(CommentLike::getCommentId, comments1.stream().map(Comment::getId).collect(Collectors.toList()))));
            int likeNum2 = CollectionUtils.isEmpty(comments2) ? 0 : Math.toIntExact(commentLikeMapper.selectCount(new LambdaQueryWrapper<CommentLike>().in(CommentLike::getCommentId, comments2.stream().map(Comment::getId).collect(Collectors.toList()))));
            for (Post post : posts1) {
                likeNum1 += post.getLikeNum();
            }
            for (Post post : posts2) {
                likeNum2 += post.getLikeNum();
            }
            user1.put("beLikeNum", likeNum1);
            user2.put("beLikeNum", likeNum2);

            // 获取被收藏量
            long favoriteNum1 = CollectionUtils.isEmpty(posts1) ? 0 : favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().in(Favorite::getPostId, posts1.stream().map(Post::getId).collect(Collectors.toList())));
            long favoriteNum2 = CollectionUtils.isEmpty(posts2) ? 0 : favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().in(Favorite::getPostId, posts2.stream().map(Post::getId).collect(Collectors.toList())));
            user1.put("beFavoriteNum", favoriteNum1);
            user2.put("beFavoriteNum", favoriteNum2);
            // 获取评论量
            Long commentSize1 = commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getUserId, user1.get("userId")));
            Long commentSize2 = commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getUserId, user2.get("userId")));
            user1.put("commentNum", commentSize1);
            user2.put("commentNum", commentSize2);
            // 获取关注量
            List<User> followUsers1 = followMapper.selectByFollowerUserId((Integer) user1.get("userId"));
            List<User> followUsers2 = followMapper.selectByFollowerUserId((Integer) user2.get("userId"));
            user1.put("followNum", followUsers1.size());
            user2.put("followNum", followUsers2.size());
            // 判断是否关注
            if (followMapper.selectCount(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowerUserId, userId).eq(Follow::getFollowedUserId, user1.get("userId"))) != 0) user1.put("isFollowing", true);
            else user1.put("isFollowing", false);
            if (followMapper.selectCount(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowerUserId, userId).eq(Follow::getFollowedUserId, user2.get("userId"))) != 0) user2.put("isFollowing", true);
            else user2.put("isFollowing", false);

            // 根据排序规则进行排序
            double hotness1 = fans1.size() * 0.3 + posts1.size() * 0.3
                    + likeNum1 * 0.15 + favoriteNum1 * 0.1
                    + commentSize1 * 0.1 + followUsers1.size() * 0.05;
            double hotness2 = fans2.size() * 0.3 + posts2.size() * 0.3
                    + likeNum2 * 0.15 + favoriteNum2 * 0.1
                    + commentSize2 * 0.1 + followUsers2.size() * 0.05;
            user1.put("hotness", hotness1);
            user2.put("hotness", hotness2);
            return Double.compare(hotness2, hotness1);
        });
        return userMapList.subList(0, count <= userMapList.size() ? count : userMapList.size());
    }

    @Override
    public List<Map<String, Object>> getHotPostList(Integer count) throws Exception {
        /*
         帖子热度 = 点赞数 * 0.3 + 收藏量 * 0.5 + 评论量 * 0.15 + 评论点赞量 * 0.05
         */
        List<Post> posts = postMapper.selectAllPost();

        // 将帖子对象列表转换为Map列表
        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(posts);
        List<Map<String, Object>> postMaps = objectMapper.readValue(s, new TypeReference<>() {
        });

        postMaps.sort((post1, post2) -> {
            // 获取收藏量
            Long favoriteSize1 = favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().eq(Favorite::getPostId, post1.get("id")));
            Long favoriteSize2 = favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().eq(Favorite::getPostId, post2.get("id")));
            post1.put("favoriteNum", favoriteSize1);
            post2.put("favoriteNum", favoriteSize2);

            // 获取评论数
            int commCnt1 = Math.toIntExact(commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getPostId, post1.get("id"))));
            int commCnt2 = Math.toIntExact(commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getPostId, post2.get("id"))));
            post1.put("commentNum", commCnt1);
            post2.put("commentNum", commCnt2);

            // 获取评论点赞总数
            List<Comment> comments1 = commentMapper.selectList(new LambdaQueryWrapper<Comment>().eq(Comment::getPostId, post1.get("id")));
            List<Comment> comments2 = commentMapper.selectList(new LambdaQueryWrapper<Comment>().eq(Comment::getPostId, post2.get("id")));
            int comLike1 = CollectionUtils.isEmpty(comments1) ? 0 : Math.toIntExact(commentLikeMapper.selectCount(new LambdaQueryWrapper<CommentLike>().in(CommentLike::getCommentId, comments1.stream().map(Comment::getId).collect(Collectors.toList()))));
            int comLike2 = CollectionUtils.isEmpty(comments2) ? 0 : Math.toIntExact(commentLikeMapper.selectCount(new LambdaQueryWrapper<CommentLike>().in(CommentLike::getCommentId, comments2.stream().map(Comment::getId).collect(Collectors.toList()))));

            // 完善帖子用户信息
            User user1 = userMapper.selectById((Integer) post1.get("userId"));
            User user2 = userMapper.selectById((Integer) post2.get("userId"));
            post1.put("userId", user1.getUserId());
            post2.put("userId", user2.getUserId());
            post1.put("nickname", user1.getNickname());
            post2.put("nickname", user2.getNickname());
            post1.put("profilePic", user1.getProfilePic());
            post2.put("profilePic", user2.getProfilePic());
            post1.put("isLike", likeMapper.selectCount(new LambdaQueryWrapper<Like>().eq(Like::getPostId, post1.get("id")).eq(Like::getUserId, user1.getUserId())) != 0);
            post2.put("isLike", likeMapper.selectCount(new LambdaQueryWrapper<Like>().eq(Like::getPostId, post2.get("id")).eq(Like::getUserId, user2.getUserId())) != 0);


            post1.put("isStaring", favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().eq(Favorite::getPostId, post1.get("id")).eq(Favorite::getUserId, user1.getUserId())) != 0);
            post2.put("isStaring", favoriteMapper.selectCount(new LambdaQueryWrapper<Favorite>().eq(Favorite::getPostId, post2.get("id")).eq(Favorite::getUserId, user2.getUserId())) != 0);

            double hotness1 = (Integer) post1.get("likeNum") * 0.3 + favoriteSize1 * 0.5
                    + commCnt1 * 0.15 + comLike1 * 0.05;
            double hotness2 = (Integer) post2.get("likeNum") * 0.3 + favoriteSize2 * 0.5
                    + commCnt2 * 0.15 + comLike2 * 0.05;
            post1.put("hotness", hotness1);
            post2.put("hotness", hotness2);
            return Double.compare(hotness2, hotness1);
        });
        return postMaps.subList(0, count <= postMaps.size() ? count : postMaps.size());
    }


    @Override
    public List<Map<String, Object>> getGeneratedHotUserList(Integer count, Integer userId) throws Exception {
        List<Map<String, Object>> list = redisUtils.getCacheObject("HOT_USER_LIST");
        if (list == null) {
            refreshHotUserListTask();
            System.out.println("Get refresh user list");
            list = redisUtils.getCacheObject("HOT_USER_LIST");
        } else {
            System.out.println("Get generated user list");
        }
        for (Map<String, Object> map : list) {

            map.put("isFollowing", followMapper.selectCount(new LambdaQueryWrapper<Follow>().eq(Follow::getFollowerUserId, userId).eq(Follow::getFollowedUserId, map.get("userId"))) != 0);
        }
        return list;
    }

    @Override
    public List<Map<String, Object>> getGeneratedHotPostList(Integer count) throws Exception {
        List<Map<String, Object>> list = redisUtils.getCacheObject("HOT_POST_LIST");
        if (list == null) {
            refreshHotPostListTask();
            System.out.println("Get refresh post list");
            list = redisUtils.getCacheObject("HOT_POST_LIST");
        } else {
            System.out.println("Get generated post list");
        }
        return list;
    }

    /**
     * 刷新用户热度排行榜定时任务
     * @throws Exception 异常
     */
    @Async
    @Scheduled(cron = "0,30 * * * *")// 每一个小时刷新一次
    public void refreshHotUserListTask() throws Exception {
        List<Map<String, Object>> hotUserList = getHotUserList(10, 8);
        redisUtils.setCacheObject("HOT_USER_LIST", hotUserList, 30, TimeUnit.MINUTES);
        log.info("刷新用户热度排行榜");
    }

    /**
     * 刷新帖子热度排行榜定时任务
     * @throws Exception 异常
     */
    @Async
    @Scheduled(cron = "0,30 * * * *") // 每一个小时刷新一次
    public void refreshHotPostListTask() throws Exception {
        List<Map<String, Object>> list = getHotPostList(10);
        redisUtils.setCacheObject("HOT_POST_LIST", list, 30, TimeUnit.MINUTES);
        log.info("刷新帖子热度排行榜");
    }
}
